package cn.com.idmy.cloud.util;

import cn.com.idmy.base.Err;
import cn.com.idmy.base.config.DefaultConfig;
import cn.com.idmy.base.model.Out;
import cn.com.idmy.base.util.Assert;
import cn.com.idmy.cloud.GlobalNameFilter;
import cn.com.idmy.cloud.GlobalValueFilter;
import cn.com.idmy.cloud.mask.Masks;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.filter.Filter;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.dromara.hutool.core.array.ArrayUtil;
import org.dromara.hutool.core.io.IoUtil;
import org.dromara.hutool.core.net.url.UrlUtil;
import org.dromara.hutool.core.text.StrUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.Objects;

@Slf4j
@NoArgsConstructor(access = lombok.AccessLevel.PRIVATE)
public class WebUtil {
    @Nullable
    public static HttpServletRequest request() {
        var attrs = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        return attrs == null ? null : attrs.getRequest();
    }

    public static void write(@NotNull HttpServletResponse response, @NotNull Object object, @NotNull HttpStatus status) throws IOException {
        var viaGateway = Objects.requireNonNull(request()).getHeader(DefaultConfig.corsDisabledHeaderName);
        if (!"true".equals(viaGateway)) {
            cors(response);
            response.setHeader(HttpHeaders.ACCESS_CONTROL_EXPOSE_HEADERS, ArrayUtil.join(DefaultConfig.accessControlExposeHeaders, ","));
        }
        response.setContentType(MediaType.APPLICATION_JSON.toString());
        response.setStatus(status.value());
        String json;
        try {
            json = JSON.toJSONString(object, new Filter[]{new GlobalNameFilter(), new GlobalValueFilter()});
        } catch (Exception e) {
            log.error("JSON转换错误", e);
            throw e;
        } finally {
            Masks.clear();
        }
        var writer = response.getWriter();
        writer.println(json);
        writer.flush();
    }

    public static void write(@NotNull HttpServletResponse response, @NotNull String str) throws IOException {
        var viaGateway = Objects.requireNonNull(request()).getHeader(DefaultConfig.corsDisabledHeaderName);
        if (!"true".equals(viaGateway)) {
            cors(response);
            response.setHeader(HttpHeaders.ACCESS_CONTROL_EXPOSE_HEADERS, ArrayUtil.join(DefaultConfig.accessControlExposeHeaders, ","));
        }
        response.setContentType(MediaType.APPLICATION_JSON.toString());
        response.setStatus(HttpStatus.OK.value());
        var writer = response.getWriter();
        writer.println(str);
        writer.flush();
    }

    public static void write(@NotNull HttpServletResponse response, @NotNull MediaType mediaType, @NotNull String filename, byte @NotNull [] bytes) throws IOException {
        var viaGateway = Objects.requireNonNull(request()).getHeader(DefaultConfig.corsDisabledHeaderName);
        if (!"true".equals(viaGateway)) {
            cors(response);
        }
        response.setHeader(HttpHeaders.ACCESS_CONTROL_EXPOSE_HEADERS, HttpHeaders.CONTENT_DISPOSITION);
        response.setHeader(HttpHeaders.CONTENT_DISPOSITION, "attachment;filename=" + UrlUtil.normalize(filename));
        response.setContentType(mediaType.toString());
        response.setStatus(HttpStatus.OK.value());
        IoUtil.write(response.getOutputStream(), true, bytes);
    }

    public static void write(@NotNull HttpServletResponse response, @NotNull Err errCode, @NotNull HttpStatus status) throws IOException {
        write(response, Out.err(errCode), status);
    }

    public static void write(@NotNull HttpServletResponse response, @NotNull Err err) throws IOException {
        write(response, Out.err(err), HttpStatus.OK);
    }

    private static void cors(@NotNull HttpServletResponse response) {
        response.setHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_CREDENTIALS, String.valueOf(DefaultConfig.accessControlAllowCredentials));
        response.setHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN, getAllowOrigin(DefaultConfig.accessControlAllowOrigin));
        response.setHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_HEADERS, ArrayUtil.join(DefaultConfig.accessControlAllowHeaders, ","));
        response.setHeader(HttpHeaders.ACCESS_CONTROL_REQUEST_METHOD, ArrayUtil.join(DefaultConfig.accessControlRequestMethods, ","));
        response.setHeader(HttpHeaders.ACCESS_CONTROL_MAX_AGE, String.valueOf(DefaultConfig.accessControlMaxAge));
        response.setHeader(HttpHeaders.CACHE_CONTROL, "no-cache");
        response.setCharacterEncoding(StandardCharsets.UTF_8.displayName());
    }

    @NotNull
    public static String getAllowOrigin(@NotNull String allowOrigin) {
        HttpServletRequest request = Assert.notNull(request(), "request为空");
        if ("*".equals(allowOrigin)) {
            String origin = request.getHeader(HttpHeaders.ORIGIN);
            return StrUtil.isBlank(origin) ? "*" : origin;
        } else {
            return allowOrigin;
        }
    }
}
